home *** CD-ROM | disk | FTP | other *** search
/ Apple WWDC 1996 / WWDC96_1996 (CD).toast / Technology Materials / QuickTime VR / MacOS / QuickDraw™ 3D 1.0.6F4 SDK / Development / 3DMF parser / 1.0 version / MF3DPC / MF3D.C < prev    next >
Encoding:
C/C++ Source or Header  |  1995-11-07  |  19.2 KB  |  638 lines  |  [TEXT/dosa]

  1. /*==============================================================================
  2.  *
  3.  *    File:        MF3D.C
  4.  *
  5.  *    Function:    QuickDraw 3D Metafile Read/Write API routines
  6.  *
  7.  *    Version:    Metafile:    Version 1.0 3DMF files
  8.  *                Package:    Release #2 of this code
  9.  *
  10.  *    Author(s):    Rick Wong (RWW), Duet Development Corp.
  11.  *                John Kelly (JRK), Duet Development Corp.
  12.  *
  13.  *    Copyright:    (c) 1995 by Apple Computer, Inc., all rights reserved.
  14.  *
  15.  *    Change History (most recent first):
  16.  *        FB8_JRK    Segmentation
  17.  *        Fabio    Changed file name to 8 characters
  18.  *        F3A_RWW    All basic types working.
  19.  *        F2S_RWW    BeginGroup changes.
  20.  *        F2R_RWW    Change to simple object theory.
  21.  *        F29_RWW    File created.
  22.  *==============================================================================
  23.  */
  24.  
  25. #include "MF3D.H"
  26.  
  27. #include <string.h>                            /* memcpy */
  28.  
  29. #include "MFERRORS.H"
  30. #include "MFINT64.H"
  31. #include "MFSTDCHK.H"
  32. #include "MFASSERT.H"
  33. #include "MFFILE.H"
  34. #include "MFGROUPS.H"
  35. #include "MFINTOBJ.H"
  36. #include "MFMACROS.H"
  37. #include "MFMEMORY.H"
  38. #include "MFOBJTYP.H"
  39. #include "MFPRIMTV.H"
  40. #include "MFRSLNTN.H"
  41. #include "MFTEXTRD.H"
  42. #include "MFTEXTWR.H"
  43.  
  44. #if defined(applec) || defined(__MWERKS__) || defined(THINK_C)
  45. #pragma segment __MF3D__
  46. #endif
  47.  
  48. /*==============================================================================
  49.  *    MF3DOpenInput
  50.  *
  51.  *    Open a metafile for parsing using caller-defined file-handling routines.
  52.  *
  53.  *    Set outMF3DFilePtr to NULL on failure.
  54.  *==============================================================================
  55.  */
  56. MF3DErr
  57. MF3DOpenInput(
  58.     MF3DUserOpenDataPtr    inUserDataPtr,        /* In:  user-defined file info    */
  59.     MF3DProcsPtr        inUserIOProcsPtr,    /* In:  user-defined I/O procs    */
  60.     MF3D_FilePtr *        outMF3DFilePtr)        /* Out: MF3D file structure        */
  61. {
  62.     MF3D_FilePtr    fileRecord;
  63.     MF3DUserFilePtr    userFilePtr;
  64.     MF3DObjType        headerSignature;
  65.     MF3DErr            result;
  66.     MF3DBoolean        fileOpened;
  67.  
  68.     result = kMF3DNoErr;
  69.  
  70.     MF3D_Allocate(fileRecord);
  71.  
  72.     if (result == kMF3DNoErr)
  73.     {    /*
  74.          * Because it is unknown at this moment whether we have
  75.          * a text file or a binary file, open it as binary.
  76.          */
  77.         result = (*inUserIOProcsPtr->openProc)(kMF3DFormatBinary,
  78.                 inUserDataPtr, &userFilePtr);
  79.     }
  80.  
  81.     if (result == kMF3DNoErr)
  82.     {    fileOpened = kMF3DBooleanTrue;
  83.         /* Read object type to determine file type */
  84.         result = (*inUserIOProcsPtr->readProc)(userFilePtr,
  85.                 sizeof(headerSignature), (char *)&headerSignature);
  86.     }
  87.  
  88.     if (result == kMF3DNoErr)
  89.     {    if (headerSignature == kMF3DObjMetafile)
  90.         {    fileRecord->dataFormat = kMF3DFormatBinary;
  91.         }
  92.         else if (headerSignature == kMF3DObjMetafileSwapped)
  93.         {    fileRecord->dataFormat = kMF3DFormatSwappedBinary;
  94.         }
  95.         else
  96.         {    /* Assume text */
  97.             fileRecord->dataFormat = kMF3DFormatText;
  98.         }
  99.     }
  100.  
  101.     /* Close and reopen as correct type
  102.      * This is unnecessary for binary files, but has the side effect
  103.      * of resetting the file offset to 0.
  104.      */
  105.     if (result == kMF3DNoErr)
  106.     {    (*inUserIOProcsPtr->closeProc)(userFilePtr);
  107.         fileOpened = kMF3DBooleanFalse;
  108.         result = (*inUserIOProcsPtr->openProc)(fileRecord->dataFormat,
  109.                 inUserDataPtr, &userFilePtr);
  110.     }
  111.  
  112.     if (result == kMF3DNoErr)
  113.     {    fileOpened = kMF3DBooleanTrue;
  114.         result = MF3D_GetPrimitivesAccessor(fileRecord->dataFormat,
  115.                 &fileRecord->primitives);
  116.     }
  117.  
  118.     if (result == kMF3DNoErr)
  119.     {    fileRecord->readWrite = MF3D_MetafileRead;
  120.         fileRecord->readBuffer.buf = NULL;
  121.         fileRecord->inContainer = 0;
  122.         fileRecord->userFilePtr = userFilePtr;
  123.         memcpy(&fileRecord->procsRec, inUserIOProcsPtr, sizeof(MF3DProcsRec));
  124.         fileRecord->resStuff.resState = MF3D_NotResolvingReference;
  125.         fileRecord->resStuff.reference = NULL;
  126.         fileRecord->resStuff.parent = NULL;
  127.         SetInt64ToZero(fileRecord->resStuff.returnLoc);
  128.         fileRecord->typeTable.nTypes = 0;
  129.         fileRecord->typeTable.types = MF3D_Malloc(0);
  130.  
  131.         result = MF3D_InitGroup(fileRecord);
  132.     }
  133.  
  134.     /* Prepare the file for reading */
  135.     if (result == kMF3DNoErr)
  136.         result = MF3D_PreprocessFile(fileRecord);
  137.  
  138.     if (result == kMF3DNoErr)
  139.     {    *outMF3DFilePtr = fileRecord;
  140.     }
  141.     else
  142.     {    if (fileOpened == kMF3DBooleanTrue)
  143.             (*inUserIOProcsPtr->closeProc)(userFilePtr);
  144.         if (fileRecord != NULL)
  145.             MF3D_Free(fileRecord);
  146.         *outMF3DFilePtr = NULL;
  147.     }
  148.  
  149.     return result;
  150. }
  151.  
  152. /*==============================================================================
  153.  *    MF3DOpenInputStdCFile
  154.  *
  155.  *    Open a metafile for parsing using standard C routines.
  156.  *
  157.  *    This routine just calls MF3DOpenInput with inUserDataPtr = inFilename
  158.  *    and inUserIOProcsPtr set to call fopen, fread, NULL, ftell, fseek,
  159.  *    and fclose.
  160.  *==============================================================================
  161.  */
  162. MF3DErr
  163. MF3DOpenInputStdCFile(
  164.     const char *    inFilename,            /* In:  C-string filename            */
  165.     MF3D_FilePtr *    outMF3DFilePtr)        /* Out: MF3D file structure            */
  166. {
  167.     MF3DProcsRec        procsRecord;
  168.     MF3DStdCOpenData    stdCOpenData;
  169.  
  170.     procsRecord.openProc = MF3DStdCOpenHook;
  171.     procsRecord.readProc = MF3DStdCReadHook;
  172.     procsRecord.writeProc = NULL;
  173.     procsRecord.tellProc = MF3DStdCTellHook;
  174.     procsRecord.seekProc = MF3DStdCSeekHook;
  175.     procsRecord.closeProc = MF3DStdCCloseHook;
  176.  
  177.     stdCOpenData.filename = (char *)inFilename;
  178.     stdCOpenData.permission = kMF3DStdCReadPerm;
  179.  
  180.     return MF3DOpenInput((MF3DUserOpenDataPtr)&stdCOpenData, &procsRecord,
  181.             outMF3DFilePtr);
  182. }
  183.  
  184. /*==============================================================================
  185.  *    MF3DReadAnObject
  186.  *
  187.  *    Retrieve the next metafile object in an open metafile.
  188.  *
  189.  *    Metafile objects are defined in <MFOBJCTS.H>. They can be recognized
  190.  *        by checking the fObjectType field and casting the object pointer
  191.  *        appropriately.
  192.  *
  193.  *    Objects returned by MF3DReadAnObject should be disposed using
  194.  *        MF3DDisposeObject when they are no longer needed. If an error occurs,
  195.  *        NULL will be returned (MF3DDisposeObject ignores NULL parameters).
  196.  *
  197.  *    Returns:
  198.  *        kMF3DNoErr                object was successfully read
  199.  *        kMF3DNoMoreObjects        the end of the file has been reached
  200.  *==============================================================================
  201.  */
  202. MF3DErr
  203. MF3DReadAnObject(
  204.     MF3D_FilePtr    inMF3DFilePtr,        /* In:  MF3D file structure            */
  205.     MF3DVoidObjPtr    *outMF3DObjPtr)        /* Out: metafile object                */
  206. {
  207.     MF3DEndContainerObjPtr    endContainerObj;
  208.     MF3DErr                    result;
  209.  
  210.     if (inMF3DFilePtr == NULL || outMF3DObjPtr == NULL)
  211.         return kMF3DErrInvalidParameter;
  212.  
  213.     if (inMF3DFilePtr->resStuff.reference != NULL)
  214.     {    /* If we are resolving a reference, jump to the referenced file */
  215.         return MF3DReadAnObject(inMF3DFilePtr->resStuff.reference,
  216.                 outMF3DObjPtr);
  217.     }
  218.     
  219.     /*
  220.      * Special upfront check for EndContainer
  221.      */
  222.     if (inMF3DFilePtr->readBuffer.buf != NULL)
  223.     {    result = MF3D_EndRead(inMF3DFilePtr);
  224.         if (result == kMF3DNoErr)
  225.         {    /* We got an end paren; it should be an EndContainer marker */
  226.             if (inMF3DFilePtr->inContainer == 0)
  227.                 result = kMF3DErrTooManyEndContainers;
  228.             else
  229.             {    --inMF3DFilePtr->inContainer;
  230.                 MF3D_Allocate(endContainerObj);
  231.             }
  232.             if (result == kMF3DNoErr)
  233.             {    endContainerObj->objectType = kMF3DObjEndContainer;
  234.                 endContainerObj->refInfo = NULL;
  235.                 *outMF3DObjPtr = (MF3DVoidObjPtr) endContainerObj;
  236.  
  237.                 /* If we were resolving a container reference, we may
  238.                  * be able to stop now.
  239.                  */
  240.                 if (inMF3DFilePtr->resStuff.resState >= MF3D_ResolvingReference)
  241.                 {    --inMF3DFilePtr->resStuff.resState;
  242.                     if (inMF3DFilePtr->resStuff.resState ==
  243.                             MF3D_ResolvingReference)
  244.                     {    MFASSERT(inMF3DFilePtr->resStuff.resState >=
  245.                                 MF3D_ResolvingReference);
  246.                         result = MF3D_PopResolution(inMF3DFilePtr);
  247.                     }
  248.                 }
  249.             }
  250.             return result;                    /* ### INTERNAL EXIT ### */
  251.         }
  252.     }
  253.  
  254.     /* This case should not happen */
  255.     MFASSERT(!(MF3DIsTextFormat(inMF3DFilePtr->dataFormat) &&
  256.             inMF3DFilePtr->readBuffer.buf != NULL));
  257.     if (MF3DIsTextFormat(inMF3DFilePtr->dataFormat) &&
  258.             inMF3DFilePtr->readBuffer.buf != NULL)
  259.     {    return kMF3DErrCantParse;            /* ### INTERNAL EXIT ### */
  260.     }
  261.  
  262.     /* Now, we want to skip TOC objects.
  263.      * NOTE: The easiest way to do this is to continue reading objects
  264.      * until we get a non-TOC object and a non-Type object.
  265.      * This is not necessarily the _best_ way.
  266.      */
  267.     do
  268.     {    result = MF3D_IntReadObject(inMF3DFilePtr, outMF3DObjPtr);
  269.         if (*outMF3DObjPtr != NULL)
  270.         {    if ((*outMF3DObjPtr)->objectType == kMF3DObjTableOfContents)
  271.                 MF3DDisposeObject(*outMF3DObjPtr);
  272.             else
  273.                 break;                        /* ### NORMAL LOOP EXIT ### */
  274.         }
  275.  
  276.         /* JRK */
  277.         /* MF3D_IntReadObject does not dispose of readBuffer on error any more.
  278.          * We need to check for it here.
  279.          */
  280.         if (result != kMF3DNoErr)
  281.         {    MF3D_Free(inMF3DFilePtr->readBuffer.buf);
  282.                 inMF3DFilePtr->readBuffer.buf = NULL;
  283.         }    
  284.     } while (result == kMF3DNoErr);
  285.  
  286.     return result;
  287. }
  288.  
  289. /*==============================================================================
  290.  *    MF3DDisposeObject
  291.  *
  292.  *    Dispose a metafile object.
  293.  *    If inMF3DObjPtr is NULL, nothing happens.
  294.  *==============================================================================
  295.  */
  296. MF3DErr
  297. MF3DDisposeObject(
  298.     MF3DVoidObjPtr    inMF3DObjPtr)        /* In:  metafile object                */
  299. {
  300.     MF3D_ObjStuffPtr    objStuff;
  301.     MF3DErr                result;
  302.  
  303.     result = kMF3DNoErr;
  304.  
  305.     if (inMF3DObjPtr != NULL)
  306.     {    result = MF3D_FindObjectFromType(inMF3DObjPtr->objectType,
  307.             &objStuff);
  308.  
  309.         if (inMF3DObjPtr->refInfo != NULL)
  310.         {    MF3D_Free(inMF3DObjPtr->refInfo->refName);
  311.             MF3D_Free(inMF3DObjPtr->refInfo);
  312.         }
  313.  
  314.         if (result == kMF3DNoErr)
  315.             result = (*objStuff->disposer) (inMF3DObjPtr);
  316.     }
  317.  
  318.     return result;
  319. }
  320.  
  321. /*==============================================================================
  322.  *    MF3DResolveReference
  323.  *
  324.  *    Set up an MF3D file so that it will read the object pointed to by
  325.  *    a reference object. The next call to MF3DReadAnObject will then read
  326.  *    the referenced object, and the MF3D file will then be reset to read the
  327.  *    object that would have been read if MF3DResolveReference had not been
  328.  *    called.
  329.  *
  330.  *    If MF3DResolveReference resolves to a group object, the entire group will
  331.  *    be read before the MF3D file is reset.
  332.  *
  333.  *    If inStoragePtr is not NULL, the reference is in an external file.
  334.  *==============================================================================
  335.  */
  336. MF3DErr
  337. MF3DResolveReference(
  338.     MF3D_FilePtr        inMF3DFilePtr,         /* In:  MF3D file structure        */
  339.     MF3DReferenceObjPtr    inMF3DRefObjPtr,     /* In:  reference object        */
  340.     MF3DStorageObjPtr    inExternalFilePtr)    /* In:    external storage object    */
  341. {
  342.     MF3DErr    result;
  343.  
  344.     if (inMF3DFilePtr == NULL || inMF3DRefObjPtr == NULL)
  345.         return kMF3DErrInvalidParameter;
  346.  
  347.     result = kMF3DNoErr;
  348.  
  349.     if (inMF3DRefObjPtr->objectType != kMF3DObjReference)
  350.         result = kMF3DErrNotAReferenceObj;
  351.  
  352.     if (result == kMF3DNoErr)
  353.     {    result = MF3D_PushResolution(inMF3DFilePtr, inMF3DRefObjPtr,
  354.                 inExternalFilePtr);
  355.     }
  356.  
  357.     return result;
  358. }
  359.  
  360. /*==============================================================================
  361.  *    MF3DClose
  362.  *
  363.  *    Close the metafile.
  364.  *
  365.  *    If inMF3DFilePtr is NULL, nothing happens.
  366.  *==============================================================================
  367.  */
  368. MF3DErr
  369. MF3DClose(
  370.     MF3D_FilePtr    inMF3DFilePtr)        /* In:  MF3D file structure            */
  371. {
  372.     MF3D_TypeListPtr    tempPtr;
  373.     MF3DUns32            numUserDefinedTypes;
  374.     MF3DErr                result, closeResult;
  375.  
  376.     if (inMF3DFilePtr == NULL)
  377.         return kMF3DNoErr;
  378.  
  379.     result = kMF3DNoErr;
  380.  
  381.     /* Free the user types table */
  382.     for (numUserDefinedTypes = inMF3DFilePtr->typeTable.nTypes,
  383.             tempPtr = inMF3DFilePtr->typeTable.types;
  384.             numUserDefinedTypes > 0;
  385.             --numUserDefinedTypes, ++tempPtr)
  386.     {    MF3D_Free(tempPtr->name);
  387.     }
  388.     MF3D_Free(inMF3DFilePtr->typeTable.types);
  389.  
  390.     if (inMF3DFilePtr->readWrite == MF3D_MetafileRead)
  391.     {    MF3D_DisposeGroup(inMF3DFilePtr);
  392.         MF3D_PostprocessFile(inMF3DFilePtr);
  393.         /* Close any files we opened to resolve a reference */
  394.         if (inMF3DFilePtr->resStuff.reference != NULL)
  395.             result = MF3D_PopResolution(inMF3DFilePtr->resStuff.reference);
  396.         
  397.         closeResult = MF3D_CloseReadBuffer(inMF3DFilePtr);
  398.  
  399.         if (result == kMF3DNoErr)
  400.             result = closeResult;
  401.     }
  402.     else if (inMF3DFilePtr->readWrite == MF3D_MetafileWrite)
  403.     {    MF3DVoidObj    fakeTOCObj;
  404.  
  405.         if (inMF3DFilePtr->tocStuff.refSeed > 1 ||
  406.                 inMF3DFilePtr->tocStuff.typeSeed < -1)
  407.         {    result = MF3D_BackpatchTOCLocation(inMF3DFilePtr);
  408.  
  409.             if (result == kMF3DNoErr)
  410.             {    /* Fake a TOC object to force it to get written */
  411.                 fakeTOCObj.objectType = kMF3DObjTableOfContents;
  412.                 fakeTOCObj.refInfo = NULL;
  413.                 result = MF3DWriteAnObject(inMF3DFilePtr, &fakeTOCObj);
  414.             }
  415.         }
  416.         MF3D_DisposeTOCStuff(inMF3DFilePtr);
  417.     }
  418.  
  419.     closeResult =
  420.             (*inMF3DFilePtr->procsRec.closeProc)(inMF3DFilePtr->userFilePtr);
  421.  
  422.     MF3D_Free(inMF3DFilePtr);
  423.  
  424.     if (result == kMF3DNoErr)
  425.         result = closeResult;
  426.  
  427.     return result;
  428. }
  429.  
  430. /*==============================================================================
  431.  *    MF3DOpenOutput
  432.  *
  433.  *    Open a metafile for writing using caller-defined file-handling routines.
  434.  *
  435.  *    Set *outMF3DFilePtr to NULL on error.
  436.  *==============================================================================
  437.  */
  438. MF3DErr
  439. MF3DOpenOutput(
  440.     MF3DDataFormatEnum    inMF3DDataFormat,    /* In:  binary or text            */
  441.     MF3DUserOpenDataPtr    inUserDataPtr,        /* In:  user-defined file info    */
  442.     MF3DProcsPtr        inUserIOProcsPtr,    /* In:  user-defined I/O procs    */
  443.     MF3D_FilePtr *        outMF3DFilePtr)        /* Out: MF3D file structure        */
  444. {
  445.     MF3D_FilePtr    fileRecord;
  446.     MF3DUserFilePtr    userFilePtr;
  447.     MF3DBoolean        fileOpened;
  448.     MF3DErr            result;
  449.  
  450.     result = kMF3DNoErr;
  451.  
  452.     MF3D_Allocate(fileRecord);
  453.  
  454.     fileOpened = kMF3DBooleanFalse;
  455.     if (result == kMF3DNoErr)
  456.     {    memcpy(&fileRecord->procsRec, inUserIOProcsPtr, sizeof(MF3DProcsRec));
  457.  
  458.         result = (*fileRecord->procsRec.openProc)(inMF3DDataFormat,
  459.                 inUserDataPtr, &userFilePtr);
  460.     }
  461.  
  462.     if (result == kMF3DNoErr)
  463.     {    fileOpened = kMF3DBooleanTrue;
  464.         result = MF3D_GetPrimitivesAccessor(inMF3DDataFormat,
  465.                 &fileRecord->primitives);
  466.     }
  467.  
  468.     if (result == kMF3DNoErr)
  469.     {    fileRecord->dataFormat = inMF3DDataFormat;
  470.         fileRecord->readWrite = MF3D_MetafileWrite;
  471.         fileRecord->userFilePtr = userFilePtr;
  472.         fileRecord->inContainer = 0;
  473.         SetInt64ToZero(fileRecord->tocLocation);
  474.         fileRecord->writeStack = NULL;
  475.         fileRecord->indent = 0;
  476.         fileRecord->tocStuff.tocLabelName = NULL;
  477.         fileRecord->tocStuff.refSeed = 1;
  478.         fileRecord->tocStuff.typeSeed = -1;
  479.         fileRecord->tocStuff.numReferences = 0;
  480.         fileRecord->tocStuff.references = MF3D_Malloc(0);
  481.         fileRecord->tocStuff.needToUpdate = kMF3DBooleanFalse;
  482.         fileRecord->typeTable.nTypes = 0;
  483.         fileRecord->typeTable.types = MF3D_Malloc(0);
  484.     }
  485.  
  486.     if (result == kMF3DNoErr)
  487.     {    *outMF3DFilePtr = fileRecord;
  488.     }
  489.     else
  490.     {    if (fileOpened)
  491.             (inUserIOProcsPtr->closeProc)(userFilePtr);
  492.         if (fileRecord != NULL)
  493.             MF3D_Free(fileRecord);
  494.         *outMF3DFilePtr = NULL;
  495.     }
  496.  
  497.     return result;
  498. }
  499.  
  500. /*==============================================================================
  501.  *    MF3DOpenOutputStdCFile
  502.  *
  503.  *    Open a metafile for writing using standard C routines.
  504.  *
  505.  *    This routine just calls MF3DOpenOutput with inUserDataPtr = inFilename
  506.  *    and inUserIOProcsPtr set to call fopen, fread, fwrite, ftell, fseek,
  507.  *    and fclose.
  508.  *==============================================================================
  509.  */
  510. MF3DErr    MF3DOpenOutputStdCFile(
  511.     MF3DDataFormatEnum    inMF3DDataFormat,    /* In:  binary or text            */
  512.     const char *        inFilename,            /* In:  C-string filename        */
  513.     MF3D_FilePtr *        outMF3DFilePtr)        /* Out: MF3D file structure        */
  514. {
  515.     MF3DProcsRec        procsRecord;
  516.     MF3DStdCOpenData    stdCOpenData;
  517.  
  518.     procsRecord.openProc = MF3DStdCOpenHook;
  519.     procsRecord.readProc = MF3DStdCReadHook;
  520.     procsRecord.writeProc = MF3DStdCWriteHook;
  521.     procsRecord.tellProc = MF3DStdCTellHook;
  522.     procsRecord.seekProc = MF3DStdCSeekHook;
  523.     procsRecord.closeProc = MF3DStdCCloseHook;
  524.  
  525.     stdCOpenData.filename = (char *)inFilename;
  526.     stdCOpenData.permission = kMF3DStdCWritePerm;
  527.  
  528.     return MF3DOpenOutput(inMF3DDataFormat, (MF3DUserOpenDataPtr)&stdCOpenData,
  529.             &procsRecord, outMF3DFilePtr);
  530. }
  531.  
  532. /*==============================================================================
  533.  *    MF3DWriteAnObject
  534.  *
  535.  *    Write a metafile object to an open metafile.
  536.  *==============================================================================
  537.  */
  538. MF3DErr
  539. MF3DWriteAnObject(
  540.     MF3D_FilePtr    inMF3DFilePtr,            /* In:  MF3D file structure        */
  541.     MF3DVoidObjPtr    inMF3DObjPtr)            /* In:  metafile object            */
  542. {
  543.     MF3D_ObjStuffPtr        objStuff;
  544.     MF3DBinaryFilePosition    location;
  545.     MF3DErr                    beginResult, writerResult, endResult;
  546.  
  547.     if (inMF3DFilePtr == NULL || inMF3DObjPtr == NULL)
  548.         return kMF3DErrInvalidParameter;
  549.  
  550.     beginResult = kMF3DNoErr;
  551.  
  552.     /* If the last object was a labeled container, set the object type
  553.      * of that label to this (the root) object.
  554.      */
  555.     if (inMF3DFilePtr->tocStuff.needToUpdate == kMF3DBooleanTrue)
  556.     {    inMF3DFilePtr->tocStuff.references[
  557.                 inMF3DFilePtr->tocStuff.numReferences - 1].type =
  558.                 inMF3DObjPtr->objectType;
  559.         inMF3DFilePtr->tocStuff.needToUpdate = kMF3DBooleanFalse;
  560.     }
  561.  
  562.     if (inMF3DObjPtr->refInfo != NULL)
  563.     {    beginResult = MF3DTellPosition(inMF3DFilePtr, &location);
  564.         if (beginResult == kMF3DNoErr)
  565.         {    beginResult = MF3D_LabelWrite(inMF3DFilePtr,
  566.                     inMF3DObjPtr->refInfo, location, inMF3DObjPtr->objectType);
  567.         }
  568.     }
  569.  
  570.     if (beginResult == kMF3DNoErr &&
  571.             inMF3DObjPtr->objectType == kMF3DObjUnknownType)
  572.     {    /* User is trying to write a new type */
  573.         beginResult = MF3D_TypeObjWrite(inMF3DFilePtr, inMF3DObjPtr);
  574.     }
  575.  
  576.     if (beginResult == kMF3DNoErr)
  577.         beginResult = MF3D_BeginWrite(inMF3DFilePtr, inMF3DObjPtr, &objStuff);
  578.  
  579.     if (beginResult == kMF3DNoErr)
  580.         writerResult = (*objStuff->writer) (inMF3DFilePtr, inMF3DObjPtr);
  581.  
  582.     /* Write the closing paren, even if writer failed */
  583.     if (beginResult == kMF3DNoErr)
  584.         endResult = MF3D_EndWrite(inMF3DFilePtr, inMF3DObjPtr);
  585.  
  586.     /* Ignore error when writing newline here */
  587.     if (beginResult == kMF3DNoErr)
  588.         MF3D_WriteNewLine(inMF3DFilePtr);
  589.  
  590.     return beginResult == kMF3DNoErr ?
  591.                 (writerResult == kMF3DNoErr ? endResult : writerResult) :
  592.                 beginResult;
  593. }
  594.  
  595. /*==============================================================================
  596.  *    MF3DTellPosition
  597.  *
  598.  *    Retrieve the position of an open metafile.
  599.  *==============================================================================
  600.  */
  601. MF3DErr
  602. MF3DTellPosition(
  603.     MF3D_FilePtr                inMF3DFilePtr,    /* In:  MF3D file structure    */
  604.     MF3DBinaryFilePosition *    outMF3DPosPtr)    /* Out: file offset            */
  605. {
  606.     MF3DErr                    result;
  607.     MF3DBinaryFilePosition    position;
  608.  
  609.     if (inMF3DFilePtr == NULL)
  610.         return kMF3DErrInvalidParameter;
  611.  
  612.     result = (*inMF3DFilePtr->procsRec.tellProc)(
  613.             inMF3DFilePtr->userFilePtr, &position);
  614.  
  615.     if (result == kMF3DNoErr)
  616.         *outMF3DPosPtr = position;
  617.  
  618.     return result;
  619. }
  620.  
  621. /*==============================================================================
  622.  *    MF3DSeekPosition
  623.  *
  624.  *    Set the position of an open metafile.
  625.  *==============================================================================
  626.  */
  627. MF3DErr
  628. MF3DSeekPosition(
  629.     MF3D_FilePtr            inMF3DFilePtr,    /* In:  MF3D file structure        */
  630.     MF3DBinaryFilePosition    inMF3DPosition)    /* In:  file offset                */
  631. {
  632.     if (inMF3DFilePtr == NULL)
  633.         return kMF3DErrInvalidParameter;
  634.  
  635.     return (*inMF3DFilePtr->procsRec.seekProc)(
  636.             inMF3DFilePtr->userFilePtr, inMF3DPosition);
  637. }
  638.